Shoreline Transformational Adaptation: Forward Looking Nature-based Climate Resilience¶

Our proposed framework recognizes the fundamental need for innovation and entrepreneurship to create the next generation of nature-inspired innovative coastal protection systems provided by the RISE Rural and Urban Coastal Community Resilience Challenge projects. To support this effort, the Mason Flood Hazards Research Lab, the Center for Ocean-Land-Atmosphere Studies and the Business for a Better World Center will: 1) develop engineering and scientific validation of the RISE innovative solutions providing the foundation for widespread implementation for coastal protection at scale; 2) provide support for a forwardlooking design strategy ensuring that the proposed alternatives are resilient to extreme events and future climate conditions; and 3) provide support to translate pilot studies to a greater market and communities and to mitigate barriers related to social acceptance and adoption.


Project Objectives¶

  1. Evaluate the performance of innovative types of nature-based solutions based on field-scale prototypes and environmental measurements and enhance the current understanding of how boat wakes and wind waves are attenuated by these interventions.

  2. Determine how these nature-based solutions help address shoreline erosion under a range of conditions including high-frequency events and extreme events.

  3. Evaluate the performance of innovative nature-based solutions for shoreline protection under a range of future climate conditions.

In [3]:
import warnings;warnings.filterwarnings("ignore")
import pathlib as pl;            import numpy as np
import netCDF4 as nc4;           import pandas as pd
import matplotlib.pyplot as plt; import xarray as xr
import json;                     import requests
import plotly.graph_objs as go;  import folium
import geopandas as gpd;
from random import randint
from chart_studio import plotly
from plotly.offline import plot,iplot, init_notebook_mode
from shapely import Polygon,Point,LineString
from rosely import WindRose
Initialize¶
In [18]:
root = pl.Path('/Users/tmiesse/work/FHRL/seagrant/field')

Task 1: Evaluation of the performance of innovative types of nature-based solutions based on field-scale prototypes and environmental measurements¶


Field sites¶

Canal¶

In [67]:
c_sensors = gpd.read_file(root / 'gps' / 'canal0721_processed' / 'canal_wgs' / 'points_wgs.shp')
c_sensors
In [86]:
m         
Out[86]:
Transect 1 - East Bank: No Wave Data¶
Transect 1 - East Bank: Currents (Change to real adcp in canal)¶
In [14]:
fig.update_layout(
    paper_bgcolor='rgba(0,0,0,0)',
    plot_bgcolor='rgba(0,0,0,0)',
    margin=dict(l=1, r=1, t=1, b=1),
    height=400,width=450,legend=dict(title='Current [m/s]'))
#fig
Transect 2 - East Bank: Waves¶
In [11]:
fig.show()
Transect 3 - West Bank: Waves¶
In [12]:
fig.show()
Transect 4 - West Bank: Waves (Missing ODU RBRs 201398,211572)¶
In [13]:
fig.show()
Transect 5 - West Bank: Waves¶
In [14]:
fig.show()
Transect 6 - North Bank: Waves¶
In [15]:
fig.show()
Canal - All sensors¶
In [16]:
fig.show()

Marsh¶

In [90]:
m         
Out[90]:
Transect 1 - Rock Sill: Waves¶
In [51]:
fig.show()
Transect 2 - Marsh Sill Gap: Waves¶
In [52]:
fig.show()
Transect 2 - Marsh Sill Gap: Currents¶
In [14]:
fig.update_layout(
    paper_bgcolor='rgba(0,0,0,0)',
    plot_bgcolor='rgba(0,0,0,0)',
    margin=dict(l=1, r=1, t=1, b=1),
    height=400,width=450,legend=dict(title='Current [m/s]'))
Transect 3 - Oyster Sill: Waves¶
In [53]:
fig.show()
Transect 4 - New Marsh/oyster¶
In [54]:
fig.show()
Marsh Site - All transects¶
In [57]:
fig.show()
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]:
 

Atmospheric Pressure¶

Atmoshperic Pressure Comparison¶

In [5]:
fnames = ['hobo0721/processed/10754554.csv',
          'hobo_0922/02_Processed/10754555.csv']
station = '8637689'
sensor = []
for f in fnames:
    file = pd.read_csv(root / 'atmosphere' / f, skiprows=1)
    atm_dt = pd.to_datetime(file['Date Time, GMT-04:00'])
    for c in file.columns:
        if 'Abs Pres' in c:
            atm    = file[c]
    sensor.append(go.Scatter(x=atm_dt,y=atm*0.689476,
                      name='FHRL'))
    start = str(atm_dt.iloc[0].year) + '{:02d}'.format(atm_dt.iloc[0].month) + '{:02d}'.format(atm_dt.iloc[0].day)
    end = str(atm_dt.iloc[-1].year) + '{:02d}'.format(atm_dt.iloc[-1].month) + '{:02d}'.format(atm_dt.iloc[-1].day)
    file = json.loads(noaa_data(start,end,station,interval='h',product='air_pressure'))
    noaa_atm = [float(data['v'])/100 for data in file['data']]
    noaa_dt  = [pd.to_datetime(data['t']) for data in file['data']]
    sensor.append(go.Scatter(x=noaa_dt,y=noaa_atm,
                      name='NOAA Yorktown'))

fig = go.Figure(data=sensor)
fig.update_layout(
    yaxis_title='Abs. Pressure [dBar]',
    margin=dict(l=10, r=10, t=10, b=10),
    font=dict(family='Times New Roman'))

fig.show()

Wave Data¶

Marsh¶

In [6]:
file = xr.open_dataset(root / 'waves' / 'rbr' / '0922' / 'processed3' / 'GMU08.nc')
file
Out[6]:
<xarray.Dataset>
Dimensions:         (Time: 16285, lat: 1, lon: 1, ele: 1, fc: 177,
                     RTime: 41336270)
Coordinates:
  * Time            (Time) float64 0.0 0.0 0.0 ... 7.391e+05 7.391e+05 7.391e+05
  * lat             (lat) float64 37.32
  * lon             (lon) float64 -76.43
  * ele             (ele) float64 -1.843
  * fc              (fc) float64 0.06055 0.06445 0.06836 ... 0.7402 0.7441 0.748
  * RTime           (RTime) float64 7.391e+05 7.391e+05 ... 7.391e+05 7.391e+05
Data variables:
    Hs              (Time) float64 ...
    Tp              (Time) timedelta64[ns] ...
    Sf              (fc, Time) float64 ...
    depth           (Time) float64 ...
    Water_Pressure  (RTime) float64 ...
    atm_pressure    (RTime) float64 ...
xarray.Dataset
    • Time: 16285
    • lat: 1
    • lon: 1
    • ele: 1
    • fc: 177
    • RTime: 41336270
    • Time
      (Time)
      float64
      0.0 0.0 0.0 ... 7.391e+05 7.391e+05
      units :
      Matlab Datenum
      array([     0.      ,      0.      ,      0.      , ..., 739144.828124,
             739144.831596, 739144.835069])
    • lat
      (lat)
      float64
      37.32
      standard_name :
      Latitude
      long_name :
      Latitude
      units :
      degrees_north
      array([37.324358])
    • lon
      (lon)
      float64
      -76.43
      standard_name :
      Longitude
      long_name :
      Longitude
      units :
      degrees_east
      array([-76.428258])
    • ele
      (ele)
      float64
      -1.843
      standard_name :
      Elevation
      long_name :
      Sensor Elevation from top of post
      units :
      m at NAVD88
      array([-1.842729])
    • fc
      (fc)
      float64
      0.06055 0.06445 ... 0.7441 0.748
      standard_name :
      Wave frequency
      long_name :
      Wave Frequency
      units :
      Hz
      array([0.060547, 0.064453, 0.068359, 0.072266, 0.076172, 0.080078, 0.083984,
             0.087891, 0.091797, 0.095703, 0.099609, 0.103516, 0.107422, 0.111328,
             0.115234, 0.119141, 0.123047, 0.126953, 0.130859, 0.134766, 0.138672,
             0.142578, 0.146484, 0.150391, 0.154297, 0.158203, 0.162109, 0.166016,
             0.169922, 0.173828, 0.177734, 0.181641, 0.185547, 0.189453, 0.193359,
             0.197266, 0.201172, 0.205078, 0.208984, 0.212891, 0.216797, 0.220703,
             0.224609, 0.228516, 0.232422, 0.236328, 0.240234, 0.244141, 0.248047,
             0.251953, 0.255859, 0.259766, 0.263672, 0.267578, 0.271484, 0.275391,
             0.279297, 0.283203, 0.287109, 0.291016, 0.294922, 0.298828, 0.302734,
             0.306641, 0.310547, 0.314453, 0.318359, 0.322266, 0.326172, 0.330078,
             0.333984, 0.337891, 0.341797, 0.345703, 0.349609, 0.353516, 0.357422,
             0.361328, 0.365234, 0.369141, 0.373047, 0.376953, 0.380859, 0.384766,
             0.388672, 0.392578, 0.396484, 0.400391, 0.404297, 0.408203, 0.412109,
             0.416016, 0.419922, 0.423828, 0.427734, 0.431641, 0.435547, 0.439453,
             0.443359, 0.447266, 0.451172, 0.455078, 0.458984, 0.462891, 0.466797,
             0.470703, 0.474609, 0.478516, 0.482422, 0.486328, 0.490234, 0.494141,
             0.498047, 0.501953, 0.505859, 0.509766, 0.513672, 0.517578, 0.521484,
             0.525391, 0.529297, 0.533203, 0.537109, 0.541016, 0.544922, 0.548828,
             0.552734, 0.556641, 0.560547, 0.564453, 0.568359, 0.572266, 0.576172,
             0.580078, 0.583984, 0.587891, 0.591797, 0.595703, 0.599609, 0.603516,
             0.607422, 0.611328, 0.615234, 0.619141, 0.623047, 0.626953, 0.630859,
             0.634766, 0.638672, 0.642578, 0.646484, 0.650391, 0.654297, 0.658203,
             0.662109, 0.666016, 0.669922, 0.673828, 0.677734, 0.681641, 0.685547,
             0.689453, 0.693359, 0.697266, 0.701172, 0.705078, 0.708984, 0.712891,
             0.716797, 0.720703, 0.724609, 0.728516, 0.732422, 0.736328, 0.740234,
             0.744141, 0.748047])
    • RTime
      (RTime)
      float64
      7.391e+05 7.391e+05 ... 7.391e+05
      array([739088.291667, 739088.291668, 739088.29167 , ..., 739148.833562,
             739148.833563, 739148.833565])
    • Hs
      (Time)
      float64
      ...
      standard_name :
      wave height
      long_name :
      Significant Wave Height
      units :
      meters
      [16285 values with dtype=float64]
    • Tp
      (Time)
      timedelta64[ns]
      ...
      standard_name :
      wave period
      long_name :
      Wave Period
      [16285 values with dtype=timedelta64[ns]]
    • Sf
      (fc, Time)
      float64
      ...
      standard_name :
      Wave Energy Spectrum
      long_name :
      Wave Energy Spectrum
      units :
      m^2/Hz
      [2882445 values with dtype=float64]
    • depth
      (Time)
      float64
      ...
      standard_name :
      water depth
      long_name :
      Water Depth
      units :
      m
      [16285 values with dtype=float64]
    • Water_Pressure
      (RTime)
      float64
      ...
      standard_name :
      Water Pressure
      long_name :
      Atmospheric Pressure subtracted from Total Pressure to get Water Pressure
      units :
      dBar
      [41336270 values with dtype=float64]
    • atm_pressure
      (RTime)
      float64
      ...
      standard_name :
      Atmospheric Pressure
      long_name :
      Atmospheric Pressure interpolated to the same time step as the Wave Sensors
      units :
      dBar
      [41336270 values with dtype=float64]
    • Time
      PandasIndex
      PandasIndex(Index([              0.0,               0.0,               0.0,
                           0.0,               0.0,               0.0,
                           0.0,               0.0,               0.0,
                           0.0,
             ...
             739144.8038187207, 739144.8072909435, 739144.8107631654,
             739144.8142353878,   739144.81770761, 739144.8211798322,
             739144.8246520545, 739144.8281242766, 739144.8315964988,
             739144.8350687207],
            dtype='float64', name='Time', length=16285))
    • lat
      PandasIndex
      PandasIndex(Index([37.324358], dtype='float64', name='lat'))
    • lon
      PandasIndex
      PandasIndex(Index([-76.428258], dtype='float64', name='lon'))
    • ele
      PandasIndex
      PandasIndex(Index([-1.842729], dtype='float64', name='ele'))
    • fc
      PandasIndex
      PandasIndex(Index([0.060546875, 0.064453125, 0.068359375, 0.072265625, 0.076171875,
             0.080078125, 0.083984375, 0.087890625, 0.091796875, 0.095703125,
             ...
             0.712890625, 0.716796875, 0.720703125, 0.724609375, 0.728515625,
             0.732421875, 0.736328125, 0.740234375, 0.744140625, 0.748046875],
            dtype='float64', name='fc', length=177))
    • RTime
      PandasIndex
      PandasIndex(Index([739088.2916666666, 739088.2916681134, 739088.2916695601,
             739088.2916710069, 739088.2916724537, 739088.2916739004,
             739088.2916753473,  739088.291676794, 739088.2916782408,
             739088.2916796874,
             ...
             739148.8335517939, 739148.8335532408, 739148.8335546874,
             739148.8335561343, 739148.8335575811, 739148.8335590278,
             739148.8335604746, 739148.8335619213, 739148.8335633681,
             739148.8335648148],
            dtype='float64', name='RTime', length=41336270))

Canal¶


ADCP Marsh¶

In [12]:
file = pd.read_csv(root / 'waves' / 'adcp_marsh' / 'ADCP105.csv',delimiter=';')
file.head()
Out[12]:
DateTime Battery Heading Pitch Roll Pressure Temperature AnalogIn1 AnalogIn2 Speed#1(0.115m) ... Speed#88(1.420m) Dir#88(1.420m) Speed#89(1.435m) Dir#89(1.435m) Speed#90(1.450m) Dir#90(1.450m) Speed#91(1.465m) Dir#91(1.465m) Speed#92(1.480m) Dir#92(1.480m)
0 7/21/2023 07:00:00 11.6 2.4 155.8 -162.1 0.240 26.58 0 0 0.657 ... 0.391 222.20 0.686 243.06 0.497 242.82 0.671 230.38 0.466 222.56
1 7/21/2023 07:10:00 11.6 2.5 155.9 -162.0 0.239 26.54 0 0 0.445 ... 0.540 230.33 0.649 234.91 0.540 224.02 0.553 221.70 0.500 226.38
2 7/21/2023 07:20:00 11.6 352.7 155.9 -162.0 0.247 26.47 0 0 0.250 ... 0.298 230.59 0.457 232.03 0.301 277.24 0.377 255.89 0.239 256.43
3 7/21/2023 07:30:00 11.6 350.3 155.9 -161.9 0.251 26.27 0 0 0.372 ... 0.397 277.23 0.316 310.25 0.258 260.39 0.210 275.19 0.154 311.05
4 7/21/2023 07:40:00 11.6 355.0 155.7 -161.9 0.246 26.03 0 0 0.368 ... 0.527 77.94 0.504 96.49 0.687 97.02 0.426 96.06 0.495 101.06

5 rows × 193 columns

In [13]:
sname, dname = [],[]
for f in file.columns:    
    if 'Speed' in f:
        sname.append(f)
    if 'Dir' in f:
        dname.append(f)
avg_speed = file[sname].mean(axis=1)
avg_dir   = file[dname].mean(axis=1)

for i in range(len(avg_dir)):
    avg_dir[i] = avg_dir[i]+180
    if avg_dir[i]>359:
        avg_dir[i] = avg_dir[i]-360
In [15]:
df[['ws','wd']].describe()
Out[15]:
ws wd
count 9097.000000 9097.000000
mean 0.052522 202.581383
std 0.063466 151.447503
min 0.000293 -0.982174
25% 0.005620 23.766087
50% 0.030837 303.947609
75% 0.087804 338.590326
max 0.739772 358.997500
In [16]:
f = folium.Figure(width=800, height=400)
m = folium.Map(location=[37.326282, -76.429166],zoom_start=15).add_to(f)
folium.TileLayer(
        tiles = 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
        attr = 'Esri',
        name = 'Esri Satellite',
        overlay = False,
        control = True
       ).add_to(m)
fg1 = folium.FeatureGroup('Marsh Site').add_to(m)
for i in range(0,len(m_sensors)):
    folium.CircleMarker((m_sensors.geometry[i].y,m_sensors.geometry[i].x),color ='#CC79A7',
                 fill_color='#CC79A7',fill_opacity=0.7,radius=3,weight=2,popup=m_sensors['Name'][i]).add_to(fg1)

fg2 = folium.FeatureGroup('Canal Site').add_to(m)
folium.LayerControl().add_to(m)
m         
Out[16]:
In [ ]:
 
In [6]:
def noaa_data(begin,end,station,vdatum='NAVD',interval='6',
                       form='json',t_zone='GMT',unit='metric',product='water_level'):
    api = f'https://tidesandcurrents.noaa.gov/api/prod/datagetter?begin_date={begin}&end_date={end}&station={station}'\
         f'&product={product}&application=NOS.COOPS.TAC.WL&datum={vdatum}&interval={interval}&time_zone={t_zone}&units={unit}&format={form}'
    data = requests.get(url=api).content.decode()
    return data
from datetime import timedelta, datetime

def datenum_to_datetime(datenum):
    """
    Convert Matlab datenum into Python datetime.
    :param datenum: Date in datenum format
    :return:        Datetime object corresponding to datenum.
    """
    if datenum < 1:
        temp = np.nan
    else:
        days = datenum % 1
        hours = days % 1 * 24
        minutes = hours % 1 * 60
        seconds = minutes % 1 * 60
        temp = datetime.fromordinal(int(datenum)) \
               + timedelta(days=int(days)) \
               + timedelta(hours=int(hours)) \
               + timedelta(minutes=int(minutes)) \
               + timedelta(seconds=round(seconds)) \
               - timedelta(days=366)
    return temp
In [ ]:
 
In [ ]: